package fr.utbm.gi.vi51.project.formations;

import java.util.ArrayList;
import java.util.Collection;

import javax.vecmath.Point2d;

public class SquareFormation extends FormationBody {

	private static final int INTER_SLOT_SPACE = 24;

	public SquareFormation(FormationOrientation orientation) {
		super();
		this.orientation = orientation;
	}

	@Override
	public Collection<FormationSlot> scaleAt(int numberOfSlots, Point2d Position) {
		ArrayList<FormationSlot> formationSlots = new ArrayList<>();
		if (numberOfSlots > 0) {
			FormationSlot leaderSlot = new FormationSlot(this);
			formationSlots.add(leaderSlot);
			// Remember the number of added slots. Each time a slot needs to be added, verify that addedSlotCount <
			// numberOfSlots. If it isn't, stop the method.
			int addedSlotCount = 1;
			// Count the number of squares
			int squareCount = squareCount(numberOfSlots);

			switch (this.orientation) {
			case NORTH:
				// Add slots square by square
				for (int i = 1; i <= squareCount; i++) {
					// Add the front middle slot
					FormationSlot frontSlot;
					if (addedSlotCount < numberOfSlots) {
						frontSlot = new FormationSlot(leaderSlot, this, addedSlotCount, 0, -INTER_SLOT_SPACE * i, 0);
						formationSlots.add(frontSlot);
						addedSlotCount++;
					} else {
						return formationSlots;
					}
					// Add front side slots
					int distanceX = 1;
					for (int j = 1; j <= 2 * i; j++) {
						if (addedSlotCount < numberOfSlots) {
							if (j % 2 == 1) {
								FormationSlot newSlot = new FormationSlot(frontSlot, this, addedSlotCount, distanceX
										* INTER_SLOT_SPACE, 0, 0);
								formationSlots.add(newSlot);
								addedSlotCount++;
							} else {
								FormationSlot newSlot = new FormationSlot(frontSlot, this, addedSlotCount, -distanceX
										* INTER_SLOT_SPACE, 0, 0);
								formationSlots.add(newSlot);
								addedSlotCount++;
								distanceX++;
							}
						} else {
							return formationSlots;
						}
					}
					// Add side slots
					distanceX--;
					int distanceY = 1;
					for (int j = 1; j <= 4 * i; j++) {
						if (addedSlotCount < numberOfSlots) {
							if (j % 2 == 1) {
								FormationSlot newSlot = new FormationSlot(frontSlot, this, addedSlotCount, distanceX
										* INTER_SLOT_SPACE, distanceY * INTER_SLOT_SPACE, 0);
								formationSlots.add(newSlot);
								addedSlotCount++;
							} else {
								FormationSlot newSlot = new FormationSlot(frontSlot, this, addedSlotCount, -distanceX
										* INTER_SLOT_SPACE, distanceY * INTER_SLOT_SPACE, 0);
								formationSlots.add(newSlot);
								addedSlotCount++;
								distanceY++;
							}
						} else {
							return formationSlots;
						}
					}
					// Get the back middle slot
					FormationSlot backSlot = new FormationSlot(leaderSlot, this, addedSlotCount, 0, INTER_SLOT_SPACE
							* i, 0);
					// Add back side slots
					distanceX--;
					if (i > 1) {
						for (int j = 1; j <= 2 * (i - 1); j++) {
							if (addedSlotCount < numberOfSlots) {
								if (j % 2 == 1) {
									FormationSlot newSlot = new FormationSlot(backSlot, this, addedSlotCount, distanceX
											* INTER_SLOT_SPACE, 0, 0);
									formationSlots.add(newSlot);
									addedSlotCount++;
								} else {
									FormationSlot newSlot = new FormationSlot(backSlot, this, addedSlotCount,
											-distanceX * INTER_SLOT_SPACE, 0, 0);
									formationSlots.add(newSlot);
									addedSlotCount++;
									distanceX--;
								}
							} else {
								return formationSlots;
							}
						}
					}
					// Add back middle slot
					if (addedSlotCount < numberOfSlots) {
						backSlot.setIndex(addedSlotCount);
						formationSlots.add(backSlot);
						addedSlotCount++;
					} else {
						return formationSlots;
					}
				}
				break;
			case SOUTH:
				// Add slots square by square
				for (int i = 1; i <= squareCount; i++) {
					// Add the front middle slot
					FormationSlot frontSlot;
					if (addedSlotCount < numberOfSlots) {
						frontSlot = new FormationSlot(leaderSlot, this, addedSlotCount, 0, INTER_SLOT_SPACE * i, 0);
						formationSlots.add(frontSlot);
						addedSlotCount++;
					} else {
						return formationSlots;
					}
					// Add front side slots
					int distanceX = 1;
					for (int j = 1; j <= 2 * i; j++) {
						if (addedSlotCount < numberOfSlots) {
							if (j % 2 == 1) {
								FormationSlot newSlot = new FormationSlot(frontSlot, this, addedSlotCount, distanceX
										* INTER_SLOT_SPACE, 0, 0);
								formationSlots.add(newSlot);
								addedSlotCount++;
							} else {
								FormationSlot newSlot = new FormationSlot(frontSlot, this, addedSlotCount, -distanceX
										* INTER_SLOT_SPACE, 0, 0);
								formationSlots.add(newSlot);
								addedSlotCount++;
								distanceX++;
							}
						} else {
							return formationSlots;
						}
					}
					// Add side slots
					distanceX--;
					int distanceY = 1;
					for (int j = 1; j <= 4 * i; j++) {
						if (addedSlotCount < numberOfSlots) {
							if (j % 2 == 1) {
								FormationSlot newSlot = new FormationSlot(frontSlot, this, addedSlotCount, distanceX
										* INTER_SLOT_SPACE, -distanceY * INTER_SLOT_SPACE, 0);
								formationSlots.add(newSlot);
								addedSlotCount++;
							} else {
								FormationSlot newSlot = new FormationSlot(frontSlot, this, addedSlotCount, -distanceX
										* INTER_SLOT_SPACE, -distanceY * INTER_SLOT_SPACE, 0);
								formationSlots.add(newSlot);
								addedSlotCount++;
								distanceY++;
							}
						} else {
							return formationSlots;
						}
					}
					// Get the back middle slot
					FormationSlot backSlot = new FormationSlot(leaderSlot, this, addedSlotCount, 0, -INTER_SLOT_SPACE
							* i, 0);
					// Add back side slots
					distanceX--;
					if (i > 1) {
						for (int j = 1; j <= 2 * (i - 1); j++) {
							if (addedSlotCount < numberOfSlots) {
								if (j % 2 == 1) {
									FormationSlot newSlot = new FormationSlot(backSlot, this, addedSlotCount, distanceX
											* INTER_SLOT_SPACE, 0, 0);
									formationSlots.add(newSlot);
									addedSlotCount++;
								} else {
									FormationSlot newSlot = new FormationSlot(backSlot, this, addedSlotCount,
											-distanceX * INTER_SLOT_SPACE, 0, 0);
									formationSlots.add(newSlot);
									addedSlotCount++;
									distanceX--;
								}
							} else {
								return formationSlots;
							}
						}
					}
					// Add back middle slot
					if (addedSlotCount < numberOfSlots) {
						backSlot.setIndex(addedSlotCount);
						formationSlots.add(backSlot);
						addedSlotCount++;
					} else {
						return formationSlots;
					}
				}
				break;
			case WEST:
				// Add slots square by square
				for (int i = 1; i <= squareCount; i++) {
					// Add the front middle slot
					FormationSlot frontSlot;
					if (addedSlotCount < numberOfSlots) {
						frontSlot = new FormationSlot(leaderSlot, this, addedSlotCount, -INTER_SLOT_SPACE * i, 0, 0);
						formationSlots.add(frontSlot);
						addedSlotCount++;
					} else {
						return formationSlots;
					}
					// Add front side slots
					int distanceY = 1;
					for (int j = 1; j <= 2 * i; j++) {
						if (addedSlotCount < numberOfSlots) {
							if (j % 2 == 1) {
								FormationSlot newSlot = new FormationSlot(frontSlot, this, addedSlotCount, 0, distanceY
										* INTER_SLOT_SPACE, 0);
								formationSlots.add(newSlot);
								addedSlotCount++;
							} else {
								FormationSlot newSlot = new FormationSlot(frontSlot, this, addedSlotCount, 0,
										-distanceY * INTER_SLOT_SPACE, 0);
								formationSlots.add(newSlot);
								addedSlotCount++;
								distanceY++;
							}
						} else {
							return formationSlots;
						}
					}
					// Add side slots
					distanceY--;
					int distanceX = 1;
					for (int j = 1; j <= 4 * i; j++) {
						if (addedSlotCount < numberOfSlots) {
							if (j % 2 == 1) {
								FormationSlot newSlot = new FormationSlot(frontSlot, this, addedSlotCount, distanceX
										* INTER_SLOT_SPACE, distanceY * INTER_SLOT_SPACE, 0);
								formationSlots.add(newSlot);
								addedSlotCount++;
							} else {
								FormationSlot newSlot = new FormationSlot(frontSlot, this, addedSlotCount, distanceX
										* INTER_SLOT_SPACE, -distanceY * INTER_SLOT_SPACE, 0);
								formationSlots.add(newSlot);
								addedSlotCount++;
								distanceX++;
							}
						} else {
							return formationSlots;
						}
					}
					// Get the back middle slot
					FormationSlot backSlot = new FormationSlot(leaderSlot, this, addedSlotCount, INTER_SLOT_SPACE * i,
							0, 0);
					// Add back side slots
					distanceY--;
					if (i > 1) {
						for (int j = 1; j <= 2 * (i - 1); j++) {
							if (addedSlotCount < numberOfSlots) {
								if (j % 2 == 1) {
									FormationSlot newSlot = new FormationSlot(backSlot, this, addedSlotCount, 0,
											distanceY * INTER_SLOT_SPACE, 0);
									formationSlots.add(newSlot);
									addedSlotCount++;
								} else {
									FormationSlot newSlot = new FormationSlot(backSlot, this, addedSlotCount, 0,
											-distanceY * INTER_SLOT_SPACE, 0);
									formationSlots.add(newSlot);
									addedSlotCount++;
									distanceY--;
								}
							} else {
								return formationSlots;
							}
						}
					}
					// Add back middle slot
					if (addedSlotCount < numberOfSlots) {
						backSlot.setIndex(addedSlotCount);
						formationSlots.add(backSlot);
						addedSlotCount++;
					} else {
						return formationSlots;
					}
				}
				break;
			default:
				// Add slots square by square
				for (int i = 1; i <= squareCount; i++) {
					// Add the front middle slot
					FormationSlot frontSlot;
					if (addedSlotCount < numberOfSlots) {
						frontSlot = new FormationSlot(leaderSlot, this, addedSlotCount, INTER_SLOT_SPACE * i, 0, 0);
						formationSlots.add(frontSlot);
						addedSlotCount++;
					} else {
						return formationSlots;
					}
					// Add front side slots
					int distanceY = 1;
					for (int j = 1; j <= 2 * i; j++) {
						if (addedSlotCount < numberOfSlots) {
							if (j % 2 == 1) {
								FormationSlot newSlot = new FormationSlot(frontSlot, this, addedSlotCount, 0, distanceY
										* INTER_SLOT_SPACE, 0);
								formationSlots.add(newSlot);
								addedSlotCount++;
							} else {
								FormationSlot newSlot = new FormationSlot(frontSlot, this, addedSlotCount, 0,
										-distanceY * INTER_SLOT_SPACE, 0);
								formationSlots.add(newSlot);
								addedSlotCount++;
								distanceY++;
							}
						} else {
							return formationSlots;
						}
					}
					// Add side slots
					distanceY--;
					int distanceX = 1;
					for (int j = 1; j <= 4 * i; j++) {
						if (addedSlotCount < numberOfSlots) {
							if (j % 2 == 1) {
								FormationSlot newSlot = new FormationSlot(frontSlot, this, addedSlotCount, -distanceX
										* INTER_SLOT_SPACE, distanceY * INTER_SLOT_SPACE, 0);
								formationSlots.add(newSlot);
								addedSlotCount++;
							} else {
								FormationSlot newSlot = new FormationSlot(frontSlot, this, addedSlotCount, -distanceX
										* INTER_SLOT_SPACE, -distanceY * INTER_SLOT_SPACE, 0);
								formationSlots.add(newSlot);
								addedSlotCount++;
								distanceX++;
							}
						} else {
							return formationSlots;
						}
					}
					// Get the back middle slot
					FormationSlot backSlot = new FormationSlot(leaderSlot, this, addedSlotCount, -INTER_SLOT_SPACE * i,
							0, 0);
					// Add back side slots
					distanceY--;
					if (i > 1) {
						for (int j = 1; j <= 2 * (i - 1); j++) {
							if (addedSlotCount < numberOfSlots) {
								if (j % 2 == 1) {
									FormationSlot newSlot = new FormationSlot(backSlot, this, addedSlotCount, 0,
											distanceY * INTER_SLOT_SPACE, 0);
									formationSlots.add(newSlot);
									addedSlotCount++;
								} else {
									FormationSlot newSlot = new FormationSlot(backSlot, this, addedSlotCount, 0,
											-distanceY * INTER_SLOT_SPACE, 0);
									formationSlots.add(newSlot);
									addedSlotCount++;
									distanceY--;
								}
							} else {
								return formationSlots;
							}
						}
					}
					// Add back middle slot
					if (addedSlotCount < numberOfSlots) {
						backSlot.setIndex(addedSlotCount);
						formationSlots.add(backSlot);
						addedSlotCount++;
					} else {
						return formationSlots;
					}
				}
				break;
			}
		}
		return formationSlots;
	}

	public static int squareCount(int numberOfSlots) {
		int count = 1;
		int max = 9;
		int i = 2;
		while (numberOfSlots > max) {
			count++;
			max += 8 * i;
			i++;
		}
		return count;
	}

}